'''A module designed to correct peoples 401 (AOL) speech impediments.
This is the second version with *better* correction.'''
############################################################################
#    Copyright (C) 2006 by Robert Small                                    #
#    SmallR2002@Gmail.com                                                  #
#                                                                          #
#    This program is free software; you can redistribute it and#or modify  #
#    it under the terms of the GNU General Public License as published by  #
#    the Free Software Foundation; either version 2 of the License, or     #
#    (at your option) any later version.                                   #
#                                                                          #
#    This program is distributed in the hope that it will be useful,       #
#    but WITHOUT ANY WARRANTY; without even the implied warranty of        #
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         #
#    GNU General Public License for more details.                          #
#                                                                          #
#    You should have received a copy of the GNU General Public License     #
#    along with this program; if not, write to the                         #
#    Free Software Foundation, Inc.,                                       #
#    59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             #
############################################################################

__module_name__        = "bccorrector"
__module_version__     = "2.0.0"
__module_description__ = "A module designed to correct peoples 401 (AOL) speech impediments."

#TODO: Adding a grammar engine to the corrector module, a basic one could replace common mistakes. An advanced version would use 'rules' to decide a) if something was valid and b) how to fix it. This is this is a hard to implement idea and no work has been started on it, or even planned.
#TODO: Adding better handling of definitions, and better storage of them.
#TODO: Relating to the above, generating a load of common definitions. This would be done using a few simple algorithms based off common mistakes like '~in' instead of '~ing'. Checks would have to be made for real words which might be mistakenly replaced, advanced versions could do context checks to ensure the right word is used.
#TODO: A possible idea is to use an algorithm to look up words and their scrambled versions and compare on context and probability, not likely to be implemented. Could be very heavy on processor usage.
#TODO: The ability to turn correction off for a channel both *permanently* in the configuration file and temporarily by command.
#TODO: Basic punctuation correction "a.b" to "a. B" etc. This will need some work and thought to avoid screwing code. 
#TODO: Caps to bolds.
#TODO: Break code down so that the same methods can be applied to any set of text.
#TODO: Add enchant/pyenchant support to automatically correct unknown results.
#TODO: Offer an option to use enchant/pyenchant instead of the normal correction.

# 
# 0.0.2  Added in a few corrections to make it play nicely with other plugins. Also added
#       into bottlecap.py the priority of the hook, hopefully this should prevent *any* nasty
#       play with other plugins/scripts/modules.
# 
# 0.0.1  Split from main module
# 

import xchat
import string #IGNORE:W0402
import sys
import os

if (sys.path.count(os.path.dirname(__file__)) == 0):
    sys.path.append(os.path.dirname(__file__))

#  Pretty much essential.
import bcsettings
import bcmisc

if (bcsettings.modules_to_load.count("bcnotify") > 0):
    import bcnotify

def correct_line(word, word_eol, userdata): #IGNORE:W0613
    '''
    Correct the incoming data.
    @param word: all the data you need to work with, chopped up into small parts.
    @type word: string
    @param word_eol: all the data you need to work with, chopped from wherever you're starting from.
    @type word_eol: string
    @param userdata: custom object to pass back.
    @type userdata: object
    '''
    if precorrection_checks(word[1]) == 0:
        context = xchat.get_context()
        
        new_word = old_correct(word)
        
        context.emit_print("Channel Message", *new_word)
        return xchat.EAT_ALL
    else:
        return xchat.EAT_NONE
    
    return 0

def old_correct(word):
    '''
    Traditional definitions list based correction.
    @param word: the string to correct.
    @type word: string
    '''
    
    if (word[1].count(" ") > 0):
        words = word[1].split(" ")
    else:
        words = word[1]
    
    newwords = words
    
    for i, w in enumerate(newwords):
        if (len(filter(string.lowercase.__contains__, w)) < len(filter(string.uppercase.__contains__, w))):
            newwords[i] = w.lower()
    
    if newwords != words:
        newwords = newwords + " [caps]"
        words = words + " [caps]"
    
    # This checks all the words.
    newwords = [bcmisc.aol.get(w.lower, w) for w in words]
    
    #This recapitalises certain things.
    newwords = [bcmisc.caps.get(w.lower(), w) for w in newwords]
    
    newwords[0] = make_capitalised(newwords[0])
    
    if newwords != words:
        #Do red letter stuff!
        for i, w in enumerate(newwords):
            if w != words[i]: newwords[i] = red_letter(w)
    
    if (bcsettings.modules_to_load.count("bcnotify") > 0):
        bcnotify.libnotify_send(bcmisc.clean_text(word[0]) + " spoke in " + xchat.get_info('channel'), "<" + bcmisc.clean_text(word[0]) + "> " + word[1], bcsettings.libnotify_urgency_cm)
    
    return newwords

def enchant_correct(word):
    '''
    Check words with 'enchant'
    @param word: what to test.
    @type word: string
    '''
    import enchant
    
    new_words = ""
    
    if len(enchant.list_languages()) == 0:
        xchat.prnt("Could not find dictionaries")
        return 1
    
    d = enchant.Dict(enchant.Dict().tag)
    
    if (word[1].count(" ") > 0):
        words = word[1].split(" ")
    else:
        words[1] = word[1]
    
    for i, w in enumerate(words):
        #TODO: Needs check for nicks/nick variations and blanks
        if (d.check(w) == False):
            opts = d.suggest(w)
            if len(opts) > 1:
                #TODO: Do something with it better than this...
                new_words = new_words + red_letter(opts[0]) + " "
            elif len(opts) == 1:
                new_words = new_words + red_letter(opts[0]) + " "
            else:
                new_words = new_words + red_letter(w) + " "
        else:
            new_words = new_words + w + " "
            
        i = i #Prevent false errors.
    return 1

def precorrection_checks(word):
    '''
    Check if something is worth correcting, correctable, etc.
    @param word: the item to test.
    @type word: string
    '''
    #Basically check if this set of words is worth checking or not.
    
    #Is it actually there?
    if (len(word) < 1):
        return 1
    
    #Presuming it exists, how many alphanumeric characters are in it?
    if (len(filter(string.lowercase.__contains__, word[1])) == 0) and (len(filter(string.uppercase.__contains__, word[1])) == 0):
        return 1
    
    #Probably a smiley
    if (len(word[1]) < 4) and (len(filter(string.punctuation.__contains__, word[1])) > 1):
        return 1
    
    #Else, seems to be ok!
    return 0

def red_letter(word):
    '''
    A quick way to make words red, or not as the case may be.
    @param word: the word we should `red letter`.
    @type word: string
    '''
    if (bcsettings.show_corrections == True):
        return  ('\x0305' + word + '\x03')
    else:
        return word

def is_url(url):
    '''
    Check if it's a URL.
    @param url: the URL.
    @type url: string
    '''
    #Firstly check if it starts with any of the normal things.
    if (True in map(url.startswith, (bcmisc.starters))):
        return 1
    
    
    #Or ends with one of the normal things...
    if (True in map(url.endswith, (bcmisc.tlds))):
        return 1
    #End if
    
    #Or has .tld/ in.
    if [x for x in url if x in bcmisc.tlds2 ]:
        return 1
    else:
        return 0
    #End if
    
    return 0

def make_capitalised(word):
    word = word.capitalize()
    return word
